﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PostSharp.CodeWeaver;
using PostSharp.CodeModel;
using PostSharp.CodeModel.Helpers;

namespace PostSharp4ViewState.Weaver
{
   internal abstract class PersistAdvice : IAdvice
   {
      protected readonly PersistTask _parent;
      protected readonly string _methodName;
      protected readonly PersistMode _mode;
      protected readonly PerTypeData _data;
      private MethodDefDeclaration _dependentMethod;
      private TypeDefDeclaration _wovenType;

      protected PersistAdvice(PersistTask parent, PersistMode mode, string methodName, PerTypeData data)
      {
         _parent = parent;
         _methodName = methodName;
         _data = data;
         _mode = mode;
      }
      
      public string MethodName
      {
         get { return _methodName; }
      }

      public abstract JoinPointKinds JointPointKind { get; }
      protected abstract void DoCreateMethod(MethodDefDeclaration methodDef);
      public void CreateMethod(TypeDefDeclaration wovenType, MethodDefDeclaration methodDef)
      {
         _dependentMethod = methodDef;
         _wovenType = wovenType;
         DoCreateMethod(methodDef);
      }
      
      #region IAdvice Members
      public int Priority
      {
         get { return 100; }
      }
      public virtual bool RequiresWeave(WeavingContext context)
      {
         if (_data.IsEmpty(_mode))
         {
            if (_wovenType != null)
            {
               _wovenType.Methods.Remove(_dependentMethod);
            }
            return false;
         }
         return true;
      }
      public abstract void Weave(WeavingContext context, InstructionBlock block);      
      #endregion

      internal static IMethod GetInheritedMethod(TypeDefDeclaration currentTypeDef, string methodName, ModuleDeclaration targetModule)
      {         
         while (currentTypeDef != null)
         {
            IMethod m = currentTypeDef.Methods.GetByName(methodName).FirstOrDefault();
            if (m != null)
            {
               INamedType translatedTypeDef = (INamedType)currentTypeDef.Translate(targetModule);
               return translatedTypeDef.Methods.GetMethod(methodName, m.Translate(targetModule), BindingOptions.Default);
            }
            if (currentTypeDef.BaseType == null)
            {
               return null;
            }
            currentTypeDef = currentTypeDef.BaseType.GetTypeDefinition();
         }
         return null;
      }
   }
}
